/****************************************************************************
 *
 * Copyright 2018 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/****************************************************************************
 * arch/arm/src/stm32l4/stm32l4_ltdc.c
 *
 *   Copyright (C) 2013-2014 Ken Pettit. All rights reserved.
 *   Authors: Ken Pettit <pettitd@gmail.com>
 *            Marco Krahl <ocram.lhark@gmail.com>
 *
 * References:
 *   STM32F429 Technical Reference Manual and Data Sheet
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <tinyara/config.h>

#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#include <tinyara/video/fb.h>
#include <tinyara/kmalloc.h>

#include <arch/chip/ltdc.h>
#include <arch/board/board.h>

#include "up_arch.h"
#include "up_internal.h"
#include "stm32l4.h"
#include "chip/stm32l4_ltdc.h"
#include "stm32l4_ltdc.h"
#include "stm32l4_dma2d.h"

/****************************************************************************
 * Pre-Processor Definitions
 ****************************************************************************/

/* Register definition ******************************************************/

#ifndef BOARD_LTDC_WIDTH
#error BOARD_LTDC_WIDTH must be defined in the board.h header file
#endif

#ifndef BOARD_LTDC_HEIGHT
#error BOARD_LTDC_HEIGHT must be defined in the board.h header file
#endif

#define STM32_LTDC_HEIGHT           BOARD_LTDC_HEIGHT
#define STM32_LTDC_WIDTH            BOARD_LTDC_WIDTH

/* Configure LTDC register */

/* LTDC_LxWHPCR register */

#define STM32_LTDC_LxWHPCR_WHSTPOS  (BOARD_LTDC_HSYNC + BOARD_LTDC_HBP - 1)
#define STM32_LTDC_LxWHPCR_WHSPPOS  (BOARD_LTDC_HSYNC + BOARD_LTDC_HBP + \
									 STM32_LTDC_WIDTH - 1)

/* LTDC_LxWVPCR register */

#define STM32_LTDC_LxWVPCR_WVSTPOS  (BOARD_LTDC_VSYNC + BOARD_LTDC_VBP - 1)
#define STM32_LTDC_LxWVPCR_WVSPPOS  (BOARD_LTDC_VSYNC + BOARD_LTDC_VBP + \
									 STM32_LTDC_HEIGHT - 1)

/* LTDC_SSCR register */

#define STM32_LTDC_SSCR_VSH          LTDC_SSCR_VSH(BOARD_LTDC_VSYNC - 1)
#define STM32_LTDC_SSCR_HSW          LTDC_SSCR_HSW(BOARD_LTDC_HSYNC - 1)

/* LTDC_BPCR register */

#define STM32_LTDC_BPCR_AVBP         LTDC_BPCR_AVBP(STM32_LTDC_LxWVPCR_WVSTPOS)
#define STM32_LTDC_BPCR_AHBP         LTDC_BPCR_AHBP(STM32_LTDC_LxWHPCR_WHSTPOS)

/* LTDC_AWCR register */

#define STM32_LTDC_AWCR_AAH          LTDC_AWCR_AAH(STM32_LTDC_LxWVPCR_WVSPPOS)
#define STM32_LTDC_AWCR_AAW          LTDC_AWCR_AAW(STM32_LTDC_LxWHPCR_WHSPPOS)

/* LTDC_TWCR register */

#define STM32_LTDC_TWCR_TOTALH       LTDC_TWCR_TOTALH(BOARD_LTDC_VSYNC + \
									 BOARD_LTDC_VBP + \
									 STM32_LTDC_HEIGHT + BOARD_LTDC_VFP - 1)
#define STM32_LTDC_TWCR_TOTALW       LTDC_TWCR_TOTALW(BOARD_LTDC_HSYNC + \
									 BOARD_LTDC_HBP + \
									 STM32_LTDC_WIDTH + BOARD_LTDC_HFP - 1)

/* Global GCR register */

/* Synchronisation and Polarity */

#define STM32_LTDC_GCR_PCPOL         BOARD_LTDC_GCR_PCPOL
#define STM32_LTDC_GCR_DEPOL         BOARD_LTDC_GCR_DEPOL
#define STM32_LTDC_GCR_VSPOL         BOARD_LTDC_GCR_VSPOL
#define STM32_LTDC_GCR_HSPOL         BOARD_LTDC_GCR_HSPOL

/* Dither */

#define STM32_LTDC_GCR_DEN           BOARD_LTDC_GCR_DEN
#define STM32_LTDC_GCR_DBW           LTDC_GCR_GBW(BOARD_LTDC_GCR_DBW)
#define STM32_LTDC_GCR_DGW           LTDC_GCR_DGW(BOARD_LTDC_GCR_DGW)
#define STN32_LTDC_GCR_DRW           LTDC_GCR_DBW(BOARD_LTDC_GCR_DRW)

/* IER register */

#define STM32_LTDC_IER_LIE           LTDC_IER_LIE
#define STM32_LTDC_IER_FUIE          !LTDC_IER_FUIE
#define STM32_LTDC_IER_TERRIE        !LTDC_IER_TERRIE
#define STM32_LTDC_IER_RRIE          LTDC_IER_RRIE

/* LIPCR register */

#define STM32_LTDC_LIPCR_LIPOS       LTDC_LIPCR_LIPOS(STM32_LTDC_TWCR_TOTALW)

/* Configuration ************************************************************/

#ifndef CONFIG_STM32L4_LTDC_DEFBACKLIGHT
#define CONFIG_STM32L4_LTDC_DEFBACKLIGHT 0xf0
#endif
#define STM32_LTDC_BACKLIGHT_OFF 0x00

/* Color/video formats */

/* Layer 1 format */

#if defined(CONFIG_STM32L4_LTDC_L1_L8)
#define STM32_LTDC_L1_BPP         8
#define STM32_LTDC_L1_COLOR_FMT   FB_FMT_RGB8
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_L8)
#define STM32_LTDC_L1CMAP
#elif defined(CONFIG_STM32L4_LTDC_L1_AL44)
#define STM32_LTDC_L1_BPP         8
#define STM32_LTDC_L1_COLOR_FMT   ???
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_AL44)
#elif defined(CONFIG_STM32L4_LTDC_L1_AL88)
#define STM32_LTDC_L1_BPP         16
#define STM32_LTDC_L1_COLOR_FMT   ???
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_AL88)
#elif defined(CONFIG_STM32L4_LTDC_L1_ARGB4444)
#define STM32_LTDC_L1_BPP         16
#define STM32_LTDC_L1_COLOR_FMT   ???
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_ARGB4444)
#elif defined(CONFIG_STM32L4_LTDC_L1_RGB565)
#define STM32_LTDC_L1_BPP         16
#define STM32_LTDC_L1_COLOR_FMT   FB_FMT_RGB16_565
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_RGB565)
#elif defined(CONFIG_STM32L4_LTDC_L1_ARGB1555)
#define STM32_LTDC_L1_BPP         16
#define STM32_LTDC_L1_COLOR_FMT   ???
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_ARGB1555)
#elif defined(CONFIG_STM32L4_LTDC_L1_RGB888)
#define STM32_LTDC_L1_BPP         24
#define STM32_LTDC_L1_COLOR_FMT   FB_FMT_RGB24
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_RGB888)
#elif defined(CONFIG_STM32L4_LTDC_L1_ARGB8888)
#define STM32_LTDC_L1_BPP         32
#define STM32_LTDC_L1_COLOR_FMT   ???
#define STM32_LTDC_L1PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_ARGB8888)
#endif

/* Layer 2 format */

#if defined(CONFIG_STM32L4_LTDC_L2_L8)
#define STM32_LTDC_L2_BPP         8
#define STM32_LTDC_L2_COLOR_FMT   FB_FMT_RGB8
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_L8)
#define STM32_LTDC_L2CMAP
#elif defined(CONFIG_STM32L4_LTDC_L2_AL44)
#define STM32_LTDC_L2_BPP         8
#define STM32_LTDC_L2_COLOR_FMT   ???
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_AL44)
#elif defined(CONFIG_STM32L4_LTDC_L2_AL88)
#define STM32_LTDC_L2_BPP         16
#define STM32_LTDC_L2_COLOR_FMT   ???
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_AL88)
#elif defined(CONFIG_STM32L4_LTDC_L2_ARGB4444)
#define STM32_LTDC_L2_BPP         16
#define STM32_LTDC_L2_COLOR_FMT   ???
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_ARGB4444)
#elif defined(CONFIG_STM32L4_LTDC_L2_RGB565)
#define STM32_LTDC_L2_BPP         16
#define STM32_LTDC_L2_COLOR_FMT   FB_FMT_RGB16_565
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_RGB565)
#elif defined(CONFIG_STM32L4_LTDC_L2_ARGB1555)
#define STM32_LTDC_L2_BPP         16
#define STM32_LTDC_L2_COLOR_FMT   ???
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_ARGB1555)
#elif defined(CONFIG_STM32L4_LTDC_L2_RGB888)
#define STM32_LTDC_L2_BPP         24
#define STM32_LTDC_L2_COLOR_FMT   FB_FMT_RGB24
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_RGB888)
#elif defined(CONFIG_STM32L4_LTDC_L2_ARGB8888)
#define STM32_LTDC_L2_BPP         32
#define STM32_LTDC_L2_COLOR_FMT   ???
#define STM32_LTDC_L2PFCR_PF      LTDC_LxPFCR_PF(LTDC_PF_ARGB8888)
#endif

/* Framebuffer sizes in bytes */

#if STM32_LTDC_L1_BPP == 8
#define STM32_L1_STRIDE (STM32_LTDC_WIDTH)
#elif STM32_LTDC_L1_BPP == 16
#define STM32_L1_STRIDE ((STM32_LTDC_WIDTH * 16 + 7) / 8)
#elif STM32_LTDC_L1_BPP == 24
#define STM32_L1_STRIDE ((STM32_LTDC_WIDTH * 24 + 7) / 8)
#elif STM32_LTDC_L1_BPP == 32
#define STM32_L1_STRIDE ((STM32_LTDC_WIDTH * 32 + 7) / 8)
#else
#error Undefined or unrecognized base resolution
#endif

/* LTDC only supports 8 bit per pixel overal */

#define STM32_LTDC_Lx_BYPP(n)        ((n) / 8)

#define STM32_L1_FBSIZE (STM32_L1_STRIDE * STM32_LTDC_HEIGHT)

#ifdef CONFIG_STM32L4_LTDC_L2
#ifndef CONFIG_STM32L4_LTDC_L2_WIDTH
#define CONFIG_STM32L4_LTDC_L2_WIDTH STM32_LTDC_WIDTH
#endif

#if CONFIG_STM32L4_LTDC_L2_WIDTH > STM32_LTDC_WIDTH
#error Width of Layer 2 exceeds the width of the display
#endif

#ifndef CONFIG_STM32L4_LTDC_L2_HEIGHT
#define CONFIG_STM32L4_LTDC_L2_HEIGHT STM32_LTDC_HEIGHT
#endif

#if CONFIG_STM32L4_LTDC_L2_HEIGHT > STM32_LTDC_HEIGHT
#error Height of Layer 2 exceeds the height of the display
#endif

#if STM32_LTDC_L2_BPP == 8
#define STM32_L2_STRIDE (CONFIG_STM32L4_LTDC_L2_WIDTH)
#elif STM32_LTDC_L2_BPP == 16
#define STM32_L2_STRIDE ((CONFIG_STM32L4_LTDC_L2_WIDTH * 16 + 7) / 8)
#elif STM32_LTDC_L2_BPP == 24
#define STM32_L2_STRIDE ((CONFIG_STM32L4_LTDC_L2_WIDTH * 24 + 7) / 8)
#elif STM32_LTDC_L2_BPP == 32
#define STM32_L2_STRIDE ((CONFIG_STM32L4_LTDC_L2_WIDTH * 32 + 7) / 8)
#else
#error Undefined or unrecognized base resolution
#endif

#define STM32_L2_FBSIZE (STM32_L2_STRIDE * CONFIG_STM32L4_LTDC_L2_HEIGHT)

#else
#define STM32_L2_FBSIZE (0)
#endif

/* Total memory used for framebuffers */

#define STM32_TOTAL_FBSIZE (STM32_L1_FBSIZE + STM32_L2_FBSIZE)

/* Debug option */

#ifdef CONFIG_STM32L4_LTDC_REGDEBUG
#define regdbg       dbg
#define regvdbg      vdbg
#else
#define regdbg(x...)
#define regvdbg(x...)
#endif

/* Preallocated LTDC framebuffers */

/* Position the framebuffer memory in the center of the memory set aside.  We
 * will use any skirts before or after the framebuffer memory as a guard against
 * wild framebuffer writes.
 */

#define STM32_LTDC_BUFFER_SIZE  CONFIG_STM32L4_LTDC_FB_SIZE
#define STM32_LTDC_BUFFER_FREE  (STM32_LTDC_BUFFER_SIZE - STM32_TOTAL_FBSIZE)
#define STM32_LTDC_BUFFER_START (CONFIG_STM32L4_LTDC_FB_BASE + STM32_LTDC_BUFFER_FREE/2)

#if STM32_LTDC_BUFFER_FREE < 0
//#error "STM32_LTDC_BUFFER_SIZE not large enough for frame buffers"
#endif

/* Layer frame buffer */

#define STM32_LTDC_BUFFER_L1     STM32_LTDC_BUFFER_START
#define STM32_LTDC_ENDBUF_L1     (STM32_LTDC_BUFFER_L1 + STM32_L1_FBSIZE)

#ifdef CONFIG_STM32L4_LTDC_L2
#define STM32_LTDC_BUFFER_L2   STM32_LTDC_ENDBUF_L1
#define STM32_LTDC_ENDBUF_L2   (STM32_LTDC_BUFFER_L2 + STM32_L2_FBSIZE)
#else
#define STM32_LTDC_ENDBUF_L2   STM32_LTDC_ENDBUF_L1
#endif

/* Layer helpers */

#ifdef CONFIG_STM32L4_LTDC_L2
#define LTDC_NLAYERS 2
#else
#define LTDC_NLAYERS 1
#endif

#define LAYER(i)     g_ltdc.layer[i]
#define LAYER_L1     g_ltdc.layer[LTDC_LAYER_L1]
#define LAYER_L2     g_ltdc.layer[LTDC_LAYER_L2]

/* Dithering */

#ifndef CONFIG_STM32L4_LTDC_DITHER_RED
#define STM32_LTDC_DITHER_RED      0
#else
#define STM32_LTDC_DITHER_RED      CONFIG_STM32L4_LTDC_DITHER_RED
#endif
#ifndef CONFIG_STM32L4_LTDC_DITHER_GREEN
#define STM32_LTDC_DITHER_GREEN    0
#else
#define STM32_LTDC_DITHER_GREEN    CONFIG_STM32L4_LTDC_DITHER_GREEN
#endif
#ifndef CONFIG_STM32L4_LTDC_DITHER_BLUE
#define STM32_LTDC_DITHER_BLUE     0
#else
#define STM32_LTDC_DITHER_BLUE     CONFIG_STM32L4_LTDC_DITHER_BLUE
#endif

/* Background color */

#ifndef CONFIG_STM32L4_LTDC_BACKCOLOR
#define STM32_LTDC_BACKCOLOR       0
#else
#define STM32_LTDC_BACKCOLOR       CONFIG_STM32L4_LTDC_BACKCOLOR
#endif

/* Internal operation flags */

#define LTDC_LAYER_SETAREA          (1 << 0)	/* Change visible area */
#define LTDC_LAYER_SETALPHAVALUE    (1 << 1)	/* Change constant alpha value */
#define LTDC_LAYER_SETBLENDMODE     (1 << 2)	/* Change blendmode */
#define LTDC_LAYER_SETCOLORKEY      (1 << 3)	/* Change color key */
#define LTDC_LAYER_ENABLECOLORKEY   (1 << 4)	/* Enable colorkey */
#define LTDC_LAYER_SETCOLOR         (1 << 5)	/* Change default color */
#define LTDC_LAYER_SETENABLE        (1 << 6)	/* Change enabled state */
#define LTDC_LAYER_ENABLE           (1 << 7)	/* Enable the layer */

/* Layer initializing state */

#define LTDC_LAYER_INIT             LTDC_LAYER_SETAREA | \
									LTDC_LAYER_SETALPHAVALUE | \
									LTDC_LAYER_SETBLENDMODE | \
									LTDC_LAYER_SETCOLORKEY | \
									LTDC_LAYER_SETCOLOR | \
									LTDC_LAYER_SETENABLE | \
									LTDC_LAYER_ENABLE

/* Blendfactor reset values for flip operation */

#define STM32_LTDC_BF1_RESET        6
#define STM32_LTDC_BF2_RESET        7

/*
 * Waits upon vertical sync
 * Note! Position is reserved in current SRCR register but not used
 */

#define LTDC_SRCR_WAIT              4

/* Calculate the size of the layers clut table */

#ifdef CONFIG_FB_CMAP
#define STM32_LTDC_CLUT_ENTRIES     256
#ifdef STM32_LTDC_L1CMAP
#define STM32_LAYER_CLUT_SIZE     1 * 3 * STM32_LTDC_CLUT_ENTRIES
#endif
#ifdef STM32_LTDC_L2CMAP
#undef  STM32_LAYER_CLUT_SIZE
#define STM32_LAYER_CLUT_SIZE     2 * 3 * STM32_LTDC_CLUT_ENTRIES
#endif
#endif

#ifndef CONFIG_FB_CMAP
#if defined(STM32_LTDC_L1CMAP) || defined(STM32_LTDC_L2CMAP)
#undef STM32_LTDC_L1CMAP
#undef STM32_LTDC_L2CMAP
#error "Enable cmap to support the configured layer format!"
#endif
#endif

/* Layer clut rgb value positioning */

#define LTDC_L1CLUT_REDOFFSET       0
#define LTDC_L1CLUT_GREENOFFSET     256
#define LTDC_L1CLUT_BLUEOFFSET      512
#define LTDC_L2CLUT_REDOFFSET       768
#define LTDC_L2CLUT_GREENOFFSET     1024
#define LTDC_L2CLUT_BLUEOFFSET      1280

/* Layer rgb clut register position */

#define LTDC_CLUT_ADD(n)            ((uint32_t)(n) << 24)
#define LTDC_CLUT_RED(n)            ((uint32_t)(n) << 16)
#define LTDC_CLUT_GREEN(n)          ((uint32_t)(n) << 8)
#define LTDC_CLUT_BLUE(n)           ((uint32_t)(n) << 0)

/****************************************************************************
 * Private Types
 ****************************************************************************/

/* This enumeration names each layer supported by the hardware */

enum stm32_layer_e {
	LTDC_LAYER_L1 = 0,			/* LCD Layer 1 */
	LTDC_LAYER_L2,				/* LCD Layer 2 */
};

/* LTDC General layer information */

struct stm32_layer_s {
#ifdef CONFIG_STM32L4_LTDC_INTERFACE
	/* LTDC interface */

	struct ltdc_layer_s ltdc;	/* Layer control structure */
#endif

	struct stm32_ltdc_s state;	/* Layer state structure */

	/* Blending */

	uint8_t opac;				/* Opacity value for blending */
	uint8_t bf1;				/* Blend factor 1 */
	uint8_t bf2;				/* Blend factor 2 */

	/* Operation */

	uint8_t operation;			/* Operation flags */
};

/* This structure provides the state of each LTDC layer */

struct stm32_state_s {
	/* Layer state */

	struct stm32_ltdc_s state[LTDC_NLAYERS];
};

/* This structure provides the overall state of the LTDC layer */

struct stm32_ltdcdev_s {
	/* Layer information */

	struct stm32_layer_s layer[LTDC_NLAYERS];
};

/* Layer cmap table description */

#ifdef STM32_LAYER_CLUT_SIZE
enum stm32_clut_e {
	LTDC_L1CLUT_RED = LTDC_L1CLUT_REDOFFSET,
	LTDC_L1CLUT_GREEN = LTDC_L1CLUT_GREENOFFSET,
	LTDC_L1CLUT_BLUE = LTDC_L1CLUT_BLUEOFFSET,
	LTDC_L2CLUT_RED = LTDC_L2CLUT_REDOFFSET,
	LTDC_L2CLUT_GREEN = LTDC_L2CLUT_GREENOFFSET,
	LTDC_L2CLUT_BLUE = LTDC_L2CLUT_BLUEOFFSET
};
#endif

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Global register operation */

static void stm32_lcd_enable(bool enable);
static void stm32_ltdc_gpioconfig(void);
static void stm32_ltdc_periphconfig(void);
static void stm32_ltdc_bgcolor(uint32_t rgb);
static void stm32_ltdc_dither(bool enable, uint8_t red, uint8_t green, uint8_t blue);
static void stm32_ltdc_interrupt(uint32_t mask, uint32_t value);
static void stm32_ltdc_reload(uint8_t value);

/* Layer and layer register operation */

static inline void stm32_ltdc_lsetopac(FAR struct stm32_layer_s *layer);
static inline void stm32_ltdc_lunsetopac(FAR struct stm32_layer_s *layer);
static inline uint8_t stm32_ltdc_lgetopac(FAR struct stm32_layer_s *layer);
static inline bool stm32_ltdc_lvalidate(FAR struct stm32_layer_s *layer);
#ifdef CONFIG_STM32L4_LTDC_INTERFACE
static int stm32_ltdc_lvalidatearea(FAR struct stm32_layer_s *layer, fb_coord_t xpos, fb_coord_t ypos, fb_coord_t xres, fb_coord_t yres, fb_coord_t srcxpos, fb_coord_t srcypos);
#endif
static void stm32_ltdc_lupdate(FAR struct stm32_layer_s *layer);

static void stm32_ltdc_lpixelformat(FAR struct stm32_layer_s *layer);
static inline void stm32_ltdc_lframebuffer(FAR struct stm32_layer_s *layer);
static void stm32_ltdc_larea(FAR struct stm32_layer_s *layer);
static void stm32_ltdc_lcolor(FAR struct stm32_layer_s *layer, uint32_t argb);
static void stm32_ltdc_lcolorkey(FAR struct stm32_layer_s *layer);
static void stm32_ltdc_lalpha(FAR struct stm32_layer_s *layer);
static void stm32_ltdc_lblendmode(FAR struct stm32_layer_s *layer, uint8_t bf1, uint8_t bf2);

#ifdef STM32_LAYER_CLUT_SIZE
static void stm32_ltdc_cmapcpy(FAR struct fb_cmap_s *dest, FAR const struct fb_cmap_s *src);
static void stm32_ltdc_lclut(FAR struct stm32_layer_s *layer);
static void stm32_ltdc_lclutenable(FAR struct stm32_layer_s *layer, bool enable);
#endif
static void stm32_ltdc_linit(int lid);
static void stm32_ltdc_lenable(FAR struct stm32_layer_s *layer);
static void stm32_ltdc_lclear(FAR struct stm32_layer_s *layer, uint16_t color);

/* Generic frame buffer interface */

static int stm32_getvideoinfo(FAR struct fb_vtable_s *vtable, struct fb_videoinfo_s *vinfo);
static int stm32_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno, struct fb_planeinfo_s *pinfo);

/* The following is provided only if the video hardware supports RGB color
 * mapping
 */

#ifdef STM32_LAYER_CLUT_SIZE
static int stm32_getcmap(struct fb_vtable_s *vtable, struct fb_cmap_s *cmap);
static int stm32_putcmap(struct fb_vtable_s *vtable, const struct fb_cmap_s *cmap);
#endif

/* ltdc interface */

#ifdef STM32_LAYER_CLUT_SIZE
static int stm32_setclut(struct ltdc_layer_s *layer, const struct fb_cmap_s *cmap);
static int stm32_getclut(struct ltdc_layer_s *layer, struct fb_cmap_s *cmap);
#endif
static int stm32_lgetvideoinfo(struct ltdc_layer_s *layer, struct fb_videoinfo_s *vinfo);
static int stm32_lgetplaneinfo(struct ltdc_layer_s *layer, int planeno, struct fb_planeinfo_s *pinfo);

#ifdef CONFIG_STM32L4_LTDC_INTERFACE
static int stm32_getlid(FAR struct ltdc_layer_s *layer, int *lid, uint32_t flag);
static int stm32_setcolor(FAR struct ltdc_layer_s *layer, uint32_t argb);
static int stm32_getcolor(FAR struct ltdc_layer_s *layer, uint32_t *argb);
static int stm32_setcolorkey(FAR struct ltdc_layer_s *layer, uint32_t argb);
static int stm32_getcolorkey(FAR struct ltdc_layer_s *layer, uint32_t *argb);
static int stm32_setalpha(FAR struct ltdc_layer_s *layer, uint8_t alpha);
static int stm32_getalpha(FAR struct ltdc_layer_s *layer, uint8_t *alpha);
static int stm32_setblendmode(FAR struct ltdc_layer_s *layer, uint32_t mode);
static int stm32_getblendmode(FAR struct ltdc_layer_s *layer, uint32_t *mode);
static int stm32_setarea(FAR struct ltdc_layer_s *layer, FAR const struct ltdc_area_s *area, fb_coord_t srcxpos, fb_coord_t srcypos);
static int stm32_getarea(FAR struct ltdc_layer_s *layer, FAR struct ltdc_area_s *area, fb_coord_t *srcxpos, fb_coord_t *srcypos);
static int stm32_update(FAR struct ltdc_layer_s *layer, uint32_t mode);

#ifdef CONFIG_STM32L4_DMA2D
static int stm32_blit(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea);
static int stm32_blend(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea);
#endif
#endif							/* CONFIG_STM32L4_LTDC_INTERFACE */
int up_ltdc_isr(int irq, uint32_t *regs);
int up_ltdc_er_isr(int irq, uint32_t *regs);

/****************************************************************************
 * Private Data
 ****************************************************************************/

/* PIO pin configurations */

static const uint32_t g_ltdcpins[] = {
	GPIO_LTDC_R4, GPIO_LTDC_R5, GPIO_LTDC_R6, GPIO_LTDC_R7,
	GPIO_LTDC_G4, GPIO_LTDC_G5, GPIO_LTDC_G6, GPIO_LTDC_G7,
	GPIO_LTDC_B4, GPIO_LTDC_B5, GPIO_LTDC_B6, GPIO_LTDC_B7,
#if BOARD_LTDC_OUTPUT_BPP > 12
	GPIO_LTDC_R3, GPIO_LTDC_G2, GPIO_LTDC_G3, GPIO_LTDC_B3,
#if BOARD_LTDC_OUTPUT_BPP > 16
	GPIO_LTDC_R2, GPIO_LTDC_B2,
#if BOARD_LTDC_OUTPUT_BPP > 18
	GPIO_LTDC_R0, GPIO_LTDC_R1, GPIO_LTDC_G0, GPIO_LTDC_G1,
	GPIO_LTDC_B0, GPIO_LTDC_B1,
#endif
#endif
#endif
	GPIO_LTDC_VSYNC, GPIO_LTDC_HSYNC, GPIO_LTDC_DE, GPIO_LTDC_CLK
};

#define STM32_LTDC_NPINCONFIGS (sizeof(g_ltdcpins) / sizeof(uint32_t))

/* This structure provides the base layer interface */

static const struct fb_vtable_s g_vtable = {
	.getvideoinfo = stm32_getvideoinfo,
	.getplaneinfo = stm32_getplaneinfo
#ifdef STM32_LAYER_CLUT_SIZE
	, .getcmap = stm32_getcmap,
	.putcmap = stm32_putcmap
#endif
};

/* The LTDC semaphore that enforces mutually exclusive access */

static sem_t g_lock;

/* The layer active state */

static uint8_t g_lactive;

#ifdef STM32_LAYER_CLUT_SIZE
/* The layers clut table entries */

static uint8_t g_clut[STM32_LAYER_CLUT_SIZE];

/* The layer 1 cmap */

#ifdef STM32_LTDC_L1CMAP
static struct fb_cmap_s g_cmap_l1 = {
	.len = STM32_LTDC_CLUT_ENTRIES,
	.red = &g_clut[LTDC_L1CLUT_RED],
	.green = &g_clut[LTDC_L1CLUT_GREEN],
	.blue = &g_clut[LTDC_L1CLUT_BLUE]
};
#endif

/* The layer 2 cmap */

#ifdef STM32_LTDC_L2CMAP
static struct fb_cmap_s g_cmap_l2 = {
	.len = STM32_LTDC_CLUT_ENTRIES,
	.red = &g_clut[LTDC_L2CLUT_RED],
	.green = &g_clut[LTDC_L2CLUT_GREEN],
	.blue = &g_clut[LTDC_L2CLUT_BLUE]
};
#endif
#endif

/* The initialized state of the overall LTDC layers */

static struct stm32_ltdcdev_s g_ltdc = {
	.layer[LTDC_LAYER_L1] = {
		.state = {
			.lid = LTDC_LAYER_L1,
			.pinfo = {
				.fbmem = (uint8_t *)STM32_LTDC_BUFFER_L1,
				.fblen = STM32_L1_FBSIZE,
				.stride = STM32_L1_STRIDE,
				.bpp = STM32_LTDC_L1_BPP
			},
			.vinfo = {
				.fmt = STM32_LTDC_L1_COLOR_FMT,
				.xres = STM32_LTDC_WIDTH,
				.yres = STM32_LTDC_HEIGHT,
				.nplanes = 1
			}
#ifdef STM32_LTDC_L1CMAP
			, .cmap = &g_cmap_l1
#endif
		}
	}
#ifdef CONFIG_STM32L4_LTDC_L2
	, .layer[LTDC_LAYER_L2] = {
		.state = {
			.lid = LTDC_LAYER_L2,
			.pinfo = {
				.fbmem = (uint8_t *)STM32_LTDC_BUFFER_L2,
				.fblen = STM32_L2_FBSIZE,
				.stride = STM32_L2_STRIDE,
				.bpp = STM32_LTDC_L2_BPP
			},
			.vinfo = {
				.fmt = STM32_LTDC_L2_COLOR_FMT,
				.xres = STM32_LTDC_WIDTH,
				.yres = STM32_LTDC_HEIGHT,
				.nplanes = 1
			}
#ifdef STM32_LTDC_L2CMAP
			, .cmap = &g_cmap_l2
#endif
		}
	}
#endif
};

/* Pixel format lookup table */

static const uint32_t stm32_fmt_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1PFCR_PF
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2PFCR_PF
#endif
};

/* Register lookup tables */

/* LTDC_LxCR */

static const uintptr_t stm32_cr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CR
#endif
};

/* LTDC_LxWHPCR */

static const uintptr_t stm32_whpcr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1WHPCR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2WHPCR
#endif
};

/* LTDC_LxWVPCR */

static const uintptr_t stm32_wvpcr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1WVPCR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2WVPCR
#endif
};

/* LTDC_LxPFCR */

static const uintptr_t stm32_pfcr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1PFCR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2PFCR
#endif
};

/* LTDC_LxDCCR */

static const uintptr_t stm32_dccr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1DCCR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2DCCR
#endif
};

/* LTDC_LxCKCR */

static const uintptr_t stm32_ckcr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CKCR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CKCR
#endif
};

/* LTDC_LxCACR */

static const uintptr_t stm32_cacr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CACR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CACR
#endif
};

/* LTDC_LxBFCR */

static const uintptr_t stm32_bfcr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1BFCR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2BFCR
#endif
};

/* LTDC_LxCFBAR */

static const uintptr_t stm32_cfbar_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CFBAR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CFBAR
#endif
};

/* LTDC_LxCFBLR */

static const uintptr_t stm32_cfblr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CFBLR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CFBLR
#endif
};

/* LTDC_LxCFBLNR */

static const uintptr_t stm32_cfblnr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CFBLNR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CFBLNR
#endif
};

/* LTDC_LxCLUTWR */

#ifdef STM32_LAYER_CLUT_SIZE
static const uintptr_t stm32_clutwr_layer_t[LTDC_NLAYERS] = {
	STM32_LTDC_L1CLUTWR
#ifdef CONFIG_STM32L4_LTDC_L2
	, STM32_LTDC_L2CLUTWR
#endif
};
#endif

/****************************************************************************
 * Public Data
 ****************************************************************************/
LTDC_HandleTypeDef hltdc;
LTDC_LayerCfgTypeDef hlayer;
/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Configure global register
 ****************************************************************************/

/****************************************************************************
 * Name: stm32_ltdc_gpioconfig
 *
 * Description:
 *   Configure GPIO pins for use with the LTDC
 *
 ****************************************************************************/

static void stm32_ltdc_gpioconfig(void)
{
	int i;

	gvdbg("Configuring pins\n");

	/* Configure each pin */

	for (i = 0; i < STM32_LTDC_NPINCONFIGS; i++) {
		regvdbg("set gpio%d = %08x\n", i, g_ltdcpins[i]);
		stm32l4_configgpio(g_ltdcpins[i]);
	}
}

/****************************************************************************
 * Name: stm32_ltdc_periphconfig
 *
 * Description:
 *   Configures the synchronous timings
 *   Configures the synchronous signals and clock polarity
 *
 ****************************************************************************/

static void stm32_ltdc_periphconfig(void)
{
	uint32_t regval;

	/* Configure GPIO's */

	stm32_ltdc_gpioconfig();

	/* Configure APB2 LTDC clock external */

	regvdbg("configured RCC_APB2ENR=%08x\n", getreg32(STM32_RCC_APB2ENR));

	/* Configure the SAI PLL external to provide the LCD_CLK */

	regvdbg("configured RCC_PLLSAI=%08x\n", getreg32(STM32_RCC_PLLSAICFGR));

	/* Configure dedicated clock external */

	regvdbg("configured RCC_DCKCFGR=%08x\n", getreg32(STM32_RCC_DCKCFGR));

	/* Configure LTDC_SSCR */

	regval = (STM32_LTDC_SSCR_VSH | STM32_LTDC_SSCR_HSW);
	regvdbg("set LTDC_SSCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_SSCR);
	regvdbg("configured LTDC_SSCR=%08x\n", getreg32(STM32_LTDC_SSCR));

	/* Configure LTDC_BPCR */

	regval = (STM32_LTDC_BPCR_AVBP | STM32_LTDC_BPCR_AHBP);
	regvdbg("set LTDC_BPCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_BPCR);
	regvdbg("configured LTDC_BPCR=%08x\n", getreg32(STM32_LTDC_BPCR));

	/* Configure LTDC_AWCR */

	regval = (STM32_LTDC_AWCR_AAH | STM32_LTDC_AWCR_AAW);
	regvdbg("set LTDC_AWCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_AWCR);
	regvdbg("configured LTDC_AWCR=%08x\n", getreg32(STM32_LTDC_AWCR));

	/* Configure LTDC_TWCR */

	regval = (STM32_LTDC_TWCR_TOTALH | STM32_LTDC_TWCR_TOTALW);
	regvdbg("set LTDC_TWCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_TWCR);
	regvdbg("configured LTDC_TWCR=%08x\n", getreg32(STM32_LTDC_TWCR));

	/* Configure LTDC_GCR */

	regval = (STM32_LTDC_GCR_PCPOL | STM32_LTDC_GCR_DEPOL | STM32_LTDC_GCR_VSPOL | STM32_LTDC_GCR_HSPOL);
	regvdbg("set LTDC_GCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_GCR);
	regvdbg("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR));
}

/****************************************************************************
 * Name: stm32_ltdc_bgcolor
 *
 * Description:
 *   Configures background color of the LCD controller.
 *
 * Parameter:
 *   rgb - RGB888 background color
 *
 ****************************************************************************/

static void stm32_ltdc_bgcolor(uint32_t rgb)
{
	regvdbg("set LTDC_BCCR=%08x\n", rgb);
	putreg32(rgb, STM32_LTDC_BCCR);
	regvdbg("configured LTDC_BCCR=%08x\n", getreg32(STM32_LTDC_BCCR));
}

/****************************************************************************
 * Name: stm32_ltdc_dither
 *
 * Description:
 *   Configures dither settings of the LCD controller.
 *
 * Parameter:
 *   enable - Enable dithering
 *   red    - Red dither width
 *   green  - Green dither width
 *   blue   - Blue dither width
 *
 ****************************************************************************/

static void stm32_ltdc_dither(bool enable, uint8_t red, uint8_t green, uint8_t blue)
{
	uint32_t regval;

	regval = getreg32(STM32_LTDC_GCR);

	if (enable == true) {
		regval |= LTDC_GCR_DEN;
	} else {
		regval &= ~LTDC_GCR_DEN;
	}

	regval &= ~(!LTDC_GCR_DEN | LTDC_GCR_DRW(0) | LTDC_GCR_DGW(0) | LTDC_GCR_DBW(0));
	regval |= (LTDC_GCR_DRW(red) | LTDC_GCR_DGW(green) | LTDC_GCR_DBW(blue));

	regvdbg("set LTDC_GCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_GCR);
	regvdbg("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR));
}

/****************************************************************************
 * Name: stm32_ltdc_interrupt
 *
 * Description:
 *   Configures specific interrupt
 *
 * Parameter:
 *   mask  - Interrupt mask
 *   value - Interrupt value
 *
 ****************************************************************************/

static void stm32_ltdc_interrupt(uint32_t mask, uint32_t value)
{
	uint32_t regval;

	/* Get interrupts */

	regval = getreg32(STM32_LTDC_IER);
	regvdbg("get LTDC_IER=%08x\n", regval);
	regval &= ~mask;

	/* Set interrupt */

	regval |= value;
	regvdbg("set LTDC_IER=%08x\n", regval);
	putreg32(regval, STM32_LTDC_IER);
	regvdbg("configured LTDC_IER=%08x\n", getreg32(STM32_LTDC_IER));

	/* Configure LTDC_LIPCR */

	regvdbg("set LTDC_LIPCR=%08x\n", STM32_LTDC_LIPCR_LIPOS);
	putreg32(STM32_LTDC_LIPCR_LIPOS, STM32_LTDC_LIPCR);
	regvdbg("configured LTDC_LIPCR=%08x\n", getreg32(STM32_LTDC_LIPCR));
}

/****************************************************************************
 * Name: stm32_ltdc_reload
 *
 * Description:
 *   Reload the layer shadow register and make layer changes visible
 *
 * Parameter:
 *   value - Reload flag (e.g. upon vertical blank or immediately)
 *
 ****************************************************************************/

static void stm32_ltdc_reload(uint8_t value)
{
	/* Check if last reload is finished */

	while ((getreg32(STM32_LTDC_SRCR) & (LTDC_SRCR_VBR | LTDC_SRCR_IMR)) != 0) {
	}

	/* Reload shadow register */

	regvdbg("set LTDC_SRCR=%08x\n", value & ~LTDC_SRCR_WAIT);
	putreg32(value & ~LTDC_SRCR_WAIT, STM32_LTDC_SRCR);
	regvdbg("configured LTDC_SRCR=%08x\n", getreg32(STM32_LTDC_SRCR));

	/* Wait until registers reloaded */

	if (value & LTDC_SRCR_WAIT) {
		while ((getreg32(STM32_LTDC_SRCR) & (value & ~LTDC_SRCR_WAIT)) != 0) {
		}
	}
}

/****************************************************************************
 * Name: stm32_global_configure
 *
 * Description:
 *   Configure background color
 *   Configure interrupts
 *   Configure dithering
 *
 ****************************************************************************/

static void stm32_global_configure(void)
{
	/* Initialize the LTDC semaphore that enforces mutually exclusive access */

	sem_init(&g_lock, 0, 1);

	/* Set the default active layer */

#ifndef CONFIG_STM32L4_LTDC_L2
	g_lactive = LTDC_LAYER_L1;
#else
	g_lactive = LTDC_LAYER_L2;
#endif

#ifdef STM32_LAYER_CLUT_SIZE
	/* cleanup clut */

	memset(g_clut, 0, sizeof(g_clut));
#endif

	/* Configure dither */

	stm32_ltdc_dither(
#ifdef CONFIG_STM32L4_LTDC_DITHER
		true,
#else
		false,
#endif
		STM32_LTDC_DITHER_RED, STM32_LTDC_DITHER_GREEN, STM32_LTDC_DITHER_BLUE);

	/* Configure background color of the controller */

	stm32_ltdc_bgcolor(STM32_LTDC_BACKCOLOR);

	/* Enable line interrupt */

	stm32_ltdc_interrupt(STM32_LTDC_IER_LIE, 1);

	/* Disable fifo underrun interrupt */

	stm32_ltdc_interrupt(STM32_LTDC_IER_FUIE, 0);

	/* Disable transfer error interrupt */

	stm32_ltdc_interrupt(STM32_LTDC_IER_TERRIE, 0);

	/* Enable register reload interrupt */

	stm32_ltdc_interrupt(STM32_LTDC_IER_RRIE, 1);
}

/****************************************************************************
 * Name: stm32_lcd_enable
 *
 * Description:
 *   Disable the LCD peripheral
 *
 * Parameter:
 *   enable - Enable or disable
 *
 ****************************************************************************/

static void stm32_lcd_enable(bool enable)
{
	uint32_t regval;

	regval = getreg32(STM32_LTDC_GCR);
	regvdbg("get LTDC_GCR=%08x\n", regval);

	if (enable == true) {
		regval |= LTDC_GCR_LTDCEN;
	} else {
		regval &= ~LTDC_GCR_LTDCEN;
	}

	regvdbg("set LTDC_GCR=%08x\n", regval);
	putreg32(regval, STM32_LTDC_GCR);
	regvdbg("configured LTDC_GCR=%08x\n", getreg32(STM32_LTDC_GCR));
}

/****************************************************************************
 * Configure layer register
 ****************************************************************************/

/****************************************************************************
 * Name: stm32_ltdc_lclutenable
 *
 * Description:
 *   Disable or enable the layer clut support
 *
 * Parameter:
 *   layer  - Reference to the layer control structure
 *   enable - Enable or disable
 *
 ****************************************************************************/

#ifdef STM32_LAYER_CLUT_SIZE
static void stm32_ltdc_lclutenable(FAR struct stm32_layer_s *layer, bool enable)
{
	uint32_t regval;

	regval = getreg32(stm32_cr_layer_t[layer->state.lid]);
	regvdbg("get LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval);

	/* Disable the clut support during update the color table */

	if (enable == true) {
		regval |= LTDC_LxCR_CLUTEN;
	} else {
		regval &= ~LTDC_LxCR_CLUTEN;
	}

	regvdbg("set LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval);
	putreg32(regval, stm32_cr_layer_t[layer->state.lid]);
}
#endif

/****************************************************************************
 * Name: stm32_ltdc_lsetopac
 *
 * Description:
 *   Helper to set the layer to opac
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static inline void stm32_ltdc_lsetopac(FAR struct stm32_layer_s *layer)
{
	layer->opac = 0xff;
}

/****************************************************************************
 * Name: stm32_ltdc_lunsetopac
 *
 * Description:
 *   Helper to set the layer opacity to the alpha value
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static inline void stm32_ltdc_lunsetopac(FAR struct stm32_layer_s *layer)
{
	layer->opac = 0;
}

/****************************************************************************
 * Name: stm32_ltdc_lgetopac
 *
 * Description:
 *   Helper to get the configured layer opacity
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static inline uint8_t stm32_ltdc_lgetopac(FAR struct stm32_layer_s *layer)
{
	return layer->opac | layer->state.alpha;
}

/****************************************************************************
 * Name: stm32_ltdc_lvalidate
 *
 * Description:
 *   Helper to check if the layer is an valid ltdc layer
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 * Return:
 *   true  - layer valid
 *   false - layer invalid
 *
 ****************************************************************************/

static inline bool stm32_ltdc_lvalidate(FAR struct stm32_layer_s *layer)
{
#ifdef CONFIG_STM32L4_LTDC_L2
	return layer == &LAYER_L1 || layer == &LAYER_L2;
#else
	return layer == &LAYER_L1;
#endif
}

/****************************************************************************
 * Name: stm32_ltdc_lvalidatearea
 *
 * Description:
 *   Check if layer coordinates out of valid area.
 *
 * Parameter:
 *   layer   - Reference to the layer control structure
 *   xpos    - top left x position of the active area
 *   ypos    - top left y position of the active area
 *   xres    - width of the active area
 *   yres    - height of teh active area
 *   srcxpos - Top left x position from where data visible in the active area
 *   srcypos - Top left y position from where data visible in the active area
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

#ifdef CONFIG_STM32L4_LTDC_INTERFACE
static int stm32_ltdc_lvalidatearea(FAR struct stm32_layer_s *layer, fb_coord_t xpos, fb_coord_t ypos, fb_coord_t xres, fb_coord_t yres, fb_coord_t srcxpos, fb_coord_t srcypos)
{
	FAR const struct fb_videoinfo_s *vinfo = &layer->state.vinfo;

	if ((xpos > vinfo->xres - 1) || (ypos > vinfo->yres - 1) || (xres > vinfo->xres - xpos) || (yres > vinfo->yres - ypos) || (srcxpos > xpos + xres - 1) || (srcypos > ypos + yres - 1)) {
		gdbg("layer coordinates out of valid area: xpos = %d > %d, ypos = %d > %d, width = %d > %d, height = %d > %d, srcxpos = %d > %d, srcypos = %d > %d", xpos, vinfo->xres - 1, ypos, vinfo->yres - 1, xres, vinfo->xres - xpos, yres, vinfo->yres - ypos, srcxpos, xpos + xres - 1, srcypos, ypos + yres - 1);

		gdbg("Returning EINVAL\n");
		return -EINVAL;
	}

	return OK;
}
#endif

/****************************************************************************
 * Name: stm32_ltdc_lupdate
 *
 * Description:
 *   Updates shadow register content depending on the layer operation flag.
 *   This made changes for the given layer visible after the next shadow
 *   register reload.
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_lupdate(FAR struct stm32_layer_s *layer)
{
	if (layer->operation & LTDC_LAYER_SETAREA) {
		/* Updates the layer horizontal and vertical position register */

		stm32_ltdc_larea(layer);
	}

	if (layer->operation & LTDC_LAYER_SETALPHAVALUE) {
		/* Updates the constant alpha register */

		stm32_ltdc_lalpha(layer);
	}

	if (layer->operation & LTDC_LAYER_SETBLENDMODE) {
		/* Update blendfactor 1 and 2 register */

		stm32_ltdc_lblendmode(layer, layer->bf1, layer->bf2);
	}

	if (layer->operation & LTDC_LAYER_SETCOLORKEY) {
		/* Update layer colorkey register */

		stm32_ltdc_lcolorkey(layer);
	}

	if (layer->operation & LTDC_LAYER_SETCOLOR) {
		/* Update layer color register */

		stm32_ltdc_lcolor(layer, layer->state.color);
	}

	if (layer->operation & LTDC_LAYER_SETENABLE) {
		/* Enable the layer */

		stm32_ltdc_lenable(layer);
	}
}

/****************************************************************************
 * Name: stm32_ltdc_larea
 *
 * Description:
 *   Change the active area of the layer
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_larea(struct stm32_layer_s *layer)
{
	uint32_t lxpos;
	uint32_t lypos;
	uint32_t whpcr;
	uint32_t wvpcr;
	FAR struct stm32_ltdc_s *priv = &layer->state;
	FAR struct ltdc_area_s *area = &priv->area;

	regvdbg("xpos = %d, ypos = %d, xres = %d, yres = %d\n", area->xpos, area->ypos, area->xres, area->yres);

	lxpos = area->xpos + (STM32_LTDC_LxWHPCR_WHSTPOS + 1);
	lypos = area->ypos + (STM32_LTDC_LxWVPCR_WVSTPOS + 1);

	/* Accumulate horizontal position */

	whpcr = LTDC_LxWHPCR_WHSTPOS(lxpos);
	whpcr |= LTDC_LxWHPCR_WHSPPOS(lxpos + area->xres - 1);

	/* Accumulate vertical position */

	wvpcr = LTDC_LxWVPCR_WVSTPOS(lypos);
	wvpcr |= LTDC_LxWVPCR_WVSPPOS(lypos + area->yres - 1);

	/* Configure LxWHPCR / LxWVPCR register */

	regvdbg("set LTDC_L%dWHPCR=%08x\n", priv->lid + 1, whpcr);
	putreg32(whpcr, stm32_whpcr_layer_t[priv->lid]);
	regvdbg("set LTDC_L%dWVPCR=%08x\n", priv->lid + 1, wvpcr);
	putreg32(wvpcr, stm32_wvpcr_layer_t[priv->lid]);

	/* Configure framebuffer */

	stm32_ltdc_lframebuffer(layer);

	/* Clear area operation flag */

	layer->operation &= ~LTDC_LAYER_SETAREA;
}

/****************************************************************************
 * Name: stm32_ltdc_lpixelformat
 *
 * Description:
 *   Set the layer pixel format.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_lpixelformat(FAR struct stm32_layer_s *layer)
{
	/* Configure PFCR register */

	regvdbg("set LTDC_L%dPFCR=%08x\n", layer->state.lid + 1, stm32_fmt_layer_t[layer->state.lid]);
	putreg32(stm32_fmt_layer_t[layer->state.lid], stm32_pfcr_layer_t[layer->state.lid]);
}

/****************************************************************************
 * Name: stm32_ltdc_framebuffer
 *
 * Description:
 *   Change layer framebuffer offset.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   Reference to the layer control structure
 *
 ****************************************************************************/

static inline void stm32_ltdc_lframebuffer(FAR struct stm32_layer_s *layer)
{
	uint32_t offset;
	uint32_t cfblr;
	FAR struct stm32_ltdc_s *priv = &layer->state;
	FAR struct ltdc_area_s *area = &priv->area;
	FAR const struct fb_planeinfo_s *pinfo = &priv->pinfo;

	/* Configure LxCFBAR register */

	/* Calculate offset position in the framebuffer */

	offset = priv->xpos * STM32_LTDC_Lx_BYPP(pinfo->bpp) + pinfo->stride * priv->ypos;

	regvdbg("set LTDC_L%dCFBAR=%08x\n", priv->lid + 1, pinfo->fbmem + offset);
	putreg32(pinfo->fbmem + offset, stm32_cfbar_layer_t[priv->lid]);

	/* Configure LxCFBLR register */

	/* Calculate line length */

	cfblr = LTDC_LxCFBLR_CFBP(pinfo->stride) | LTDC_LxCFBLR_CFBLL(area->xres * STM32_LTDC_Lx_BYPP(pinfo->bpp) + 3);

	regvdbg("set LTDC_L%dCFBLR=%08x\n", priv->lid + 1, cfblr);
	putreg32(cfblr, stm32_cfblr_layer_t[priv->lid]);

	/* Configure LxCFBLNR register */

	regvdbg("set LTDC_L%dCFBLNR=%08x\n", priv->lid + 1, area->yres);
	putreg32(area->yres, stm32_cfblnr_layer_t[priv->lid]);
}

/****************************************************************************
 * Name: stm32_ltdc_lalpha
 *
 * Description:
 *   Change the layer alpha value and clear the alpha operation flag.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_lalpha(FAR struct stm32_layer_s *layer)
{
	uint8_t opac = stm32_ltdc_lgetopac(layer);
	regvdbg("set LTDC_L%dCACR=%02x\n", layer->state.lid + 1, opac);
	putreg32(opac, stm32_cacr_layer_t[layer->state.lid]);

	/* Clear the constant alpha operation flag */

	layer->operation &= ~LTDC_LAYER_SETALPHAVALUE;
}

/****************************************************************************
 * Name: stm32_ltdc_blendfactor
 *
 * Description:
 *   Change layer blend factors used for blend operation and clear the
 *   blendmode operation flag.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   layer - Reference to the laxer control structure
 *   bf1   - Value of blend factor 1
 *   bf2   - Value of blend factor 2
 *
 ****************************************************************************/

static void stm32_ltdc_lblendmode(FAR struct stm32_layer_s *layer, uint8_t bf1, uint8_t bf2)
{
	regvdbg("set LTDC_L%dBFCR=%08x\n", layer->state.lid + 1, (LTDC_LxBFCR_BF1(bf1) | LTDC_LxBFCR_BF2(bf2)));
	putreg32((LTDC_LxBFCR_BF1(bf1) | LTDC_LxBFCR_BF2(bf2)), stm32_bfcr_layer_t[layer->state.lid]);

	/* Clear the blendmode operation flag */

	layer->operation &= ~LTDC_LAYER_SETBLENDMODE;
}

/****************************************************************************
 * Name: stm32_ltdc_lcolor
 *
 * Description:
 *   Change layer default color and clear the color operation flag.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_lcolor(FAR struct stm32_layer_s *layer, uint32_t argb)
{
	regvdbg("set LTDC_L%dDCCR=%08x\n", layer->state.lid + 1, argb);
	putreg32(argb, stm32_dccr_layer_t[layer->state.lid]);

	/* Clear the color operation flag */

	layer->operation &= ~LTDC_LAYER_SETCOLOR;
}

/****************************************************************************
 * Name: stm32_ltdc_lcolorkey
 *
 * Description:
 *   Change layer colorkey and clear the colorkey operation flag.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_lcolorkey(FAR struct stm32_layer_s *layer)
{
	uint32_t regval;

	regval = getreg32(stm32_cr_layer_t[layer->state.lid]);

	if (layer->operation & LTDC_LAYER_ENABLECOLORKEY) {
		/* Set colorkey */

		regvdbg("set LTDC_L%dCKCR=%08x\n", layer->state.lid + 1, layer->state.colorkey);
		putreg32(layer->state.colorkey, stm32_ckcr_layer_t[layer->state.lid]);

		/* Enable colorkey */

		regval |= LTDC_LxCR_COLKEN;
	} else {
		/* Disable colorkey */

		regval &= ~LTDC_LxCR_COLKEN;
	}

	regvdbg("set LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval);
	putreg32(regval, stm32_cr_layer_t[layer->state.lid]);

	/* Clear the colorkey operation flag */

	layer->operation &= ~LTDC_LAYER_SETCOLORKEY;
}

/****************************************************************************
 * Name: stm32_ltdc_lclut
 *
 * Description:
 *   Update the clut layer register during blank period.
 *   Note! The clut register are no shadow register.
 *
 * Parameter:
 *   layer  - Reference to the layer control structure
 *
 ****************************************************************************/

#ifdef STM32_LAYER_CLUT_SIZE
static void stm32_ltdc_lclut(FAR struct stm32_layer_s *layer)
{
	int n;
	uint32_t regval;
	FAR struct fb_cmap_s *cmap = layer->state.cmap;
	irqstate_t flags;

	/* Disable clut during register update */

	stm32_ltdc_lclutenable(layer, false);

	/* Reload shadow control register.
	 * This never changed any layer setting as long the layer register not up to
	 * date. This is what stm32_update does.
	 */

	stm32_ltdc_reload(LTDC_SRCR_IMR);

	flags = irqsave();

	/* Update the clut registers */

	for (n = cmap->first; n < cmap->len - cmap->first; n++) {
		regval = (uint32_t)LTDC_CLUT_ADD(n) | (uint32_t)LTDC_CLUT_RED(cmap->red[n]) | (uint32_t)LTDC_CLUT_GREEN(cmap->green[n]) | (uint32_t)LTDC_CLUT_BLUE(cmap->blue[n]);

		regvdbg("set LTDC_L%dCLUTWR = %08x, cmap->first = %d, cmap->len = %d\n", layer->state.lid + 1, regval, cmap->first, cmap->len);
		putreg32(regval, stm32_clutwr_layer_t[layer->state.lid]);
	}

	irqrestore(flags);

	/* Enable clut */

	stm32_ltdc_lclutenable(layer, true);

	/* Reload shadow control register */

	stm32_ltdc_reload(LTDC_SRCR_IMR);
}
#endif

/****************************************************************************
 * Name: stm32_ltdc_lenable
 *
 * Description:
 *   Disable or enable specific layer.
 *   Note! This changes have no effect until the shadow register reload has
 *   been done.
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_lenable(FAR struct stm32_layer_s *layer)
{
	uint32_t regval;

	/* Enable or disable layer */

	regval = getreg32(stm32_cr_layer_t[layer->state.lid]);

	if (layer->operation & LTDC_LAYER_ENABLE) {
		regval |= LTDC_LxCR_LEN;
	} else {
		regval &= ~LTDC_LxCR_LEN;
	}

	regvdbg("set LTDC_L%dCR=%08x\n", layer->state.lid + 1, regval);
	putreg32(regval, stm32_cr_layer_t[layer->state.lid]);

	/* Clear the enable operation flag */

	layer->operation &= ~LTDC_LAYER_SETENABLE;
}

/****************************************************************************
 * Name stm32_ltdc_lclear
 *
 * Description:
 *   Clear the whole layer
 *
 * Parameter:
 *   layer - Reference to the layer control structure
 *   color - The color to clear
 *
 * Return:
 *   OK      - On success
 *   -EINVAL - If one of the parameter invalid
 *
 ****************************************************************************/

static void stm32_ltdc_lclear(FAR struct stm32_layer_s *layer, uint16_t color)
{
	FAR struct stm32_ltdc_s *priv = &layer->state;

#if STM32_LTDC_L1_BPP == 8 || STM32_LTDC_L2_BPP == 8
	if (priv->pinfo.bpp == 8) {
		uint8_t *dest = (uint8_t *)priv->pinfo.fbmem;
		int i;

		gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen);

		for (i = 0; i < priv->pinfo.fblen; i += sizeof(uint8_t)) {
			*dest++ = (uint8_t)color;
		}

		return;
	}
#endif

#if STM32_LTDC_L1_BPP == 16 || STM32_LTDC_L2_BPP == 16
	if (priv->pinfo.bpp == 16) {
		uint16_t *dest = (uint16_t *)priv->pinfo.fbmem;
		int i;

		gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen);

		for (i = 0; i < priv->pinfo.fblen; i += sizeof(uint16_t)) {
			*dest++ = (uint16_t)color;
		}

		return;
	}
#endif

#if STM32_LTDC_L1_BPP == 24 || STM32_LTDC_L2_BPP == 24
	if (priv->pinfo.bpp == 24) {
		uint8_t *dest = (uint8_t *)priv->pinfo.fbmem;
		uint8_t r;
		uint8_t g;
		uint8_t b;
		int i;

		gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen);

		r = (uint8_t)color;
		g = (uint8_t)(color >> 8);
		b = (uint8_t)(color >> 16);

		for (i = 0; i < priv->pinfo.fblen; i += 3 * sizeof(uint8_t)) {
			*dest++ = r;
			*dest++ = g;
			*dest++ = b;
		}

		return;
	}
#endif

#if STM32_LTDC_L1_BPP == 32 || STM32_LTDC_L2_BPP == 32
	if (priv->pinfo.bpp == 32) {
		uint32_t *dest = (uint32_t *)priv->pinfo.fbmem;
		int i;

		gvdbg("Clearing display: BPP=%d color=%04x framebuffer=%08x size=%d\n", priv->pinfo.bpp, color, dest, priv->pinfo.fblen);

		for (i = 0; i < priv->pinfo.fblen; i += sizeof(uint32_t)) {
			*dest++ = (uint32_t)color;
		}
	}
#endif
}

/****************************************************************************
 * Name: stm32_ltdc_linit
 *
 * Description:
 *   Initialize layer to their default states.
 *
 *   Initialize:
 *   - Reset layer
 *   - layer fram
 *   - Reset layerebuffers
 *   - layer position
 *   - layer pixelformat
 *   - layer color
 *   - layer colorkey
 *   - layer alpha
 *   - layer blendmode
 *
 * Parameter
 *   layer - Reference to the layer control structure
 *
 ****************************************************************************/

static void stm32_ltdc_linit(int lid)
{
	/* Reset layer to their default state */

	FAR struct stm32_layer_s *layer = &LAYER(lid);
	FAR struct stm32_ltdc_s *state = &layer->state;
#ifdef CONFIG_STM32L4_LTDC_INTERFACE
	FAR struct ltdc_layer_s *ltdc = &layer->ltdc;

	/* Initialize the ltdc interface */

	ltdc->getlid = stm32_getlid;
	ltdc->getvideoinfo = stm32_lgetvideoinfo;
	ltdc->getplaneinfo = stm32_lgetplaneinfo;
#ifdef STM32_LAYER_CLUT_SIZE
	ltdc->setclut = stm32_setclut;
	ltdc->getclut = stm32_getclut;
#endif
	ltdc->setcolor = stm32_setcolor;
	ltdc->getcolor = stm32_getcolor;
	ltdc->setcolorkey = stm32_setcolorkey;
	ltdc->getcolorkey = stm32_getcolorkey;
	ltdc->setalpha = stm32_setalpha;
	ltdc->getalpha = stm32_getalpha;
	ltdc->setblendmode = stm32_setblendmode;
	ltdc->getblendmode = stm32_getblendmode;
	ltdc->setarea = stm32_setarea;
	ltdc->getarea = stm32_getarea;
	ltdc->update = stm32_update;
#ifdef CONFIG_STM32L4_DMA2D
	ltdc->blit = stm32_blit;
	ltdc->blend = stm32_blend;
#endif
#endif

	/* Initialize the layer state */

#ifdef STM32_LAYER_CLUT_SIZE
	state->cmap->first = 0;
#endif
	state->area.xpos = 0;
	state->area.ypos = 0;
	state->area.xres = STM32_LTDC_WIDTH;
	state->area.yres = STM32_LTDC_HEIGHT;
	state->xpos = 0;
	state->ypos = 0;
	state->color = 0;
	state->colorkey = 0;
	state->alpha = 0xff;
	state->blendmode = LTDC_BLEND_NONE;
	state->lock = &g_lock;

	/* Initialize driver internals */

	layer->opac = 0xff;
	layer->bf1 = LTDC_BF1_CONST_ALPHA;
	layer->bf2 = LTDC_BF2_CONST_ALPHA;
	layer->operation = LTDC_LAYER_INIT;

	/* Clear the layer framebuffer */

	stm32_ltdc_lclear(layer, 0);

	/* Set Pixel input format */

	stm32_ltdc_lpixelformat(layer);

	/* Set position, color, colorkey, blendmode, alpha */

	stm32_ltdc_lupdate(layer);

#ifdef STM32_LAYER_CLUT_SIZE
	/* Disable clut by default */

	if (layer->state.vinfo.fmt == FB_FMT_RGB8) {
		stm32_ltdc_lclutenable(layer, false);
	}
#endif
}

/****************************************************************************
 * Name: stm32_ltdc_cmapcpy
 *
 * Description:
 *   Copies a color table
 *   Note! Caller must check if first position is out of range
 *
 * Parameter:
 *   dest - color cmap to copy to
 *   src  - color map to copy from
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

#ifdef STM32_LAYER_CLUT_SIZE
static void stm32_ltdc_cmapcpy(FAR struct fb_cmap_s *dest, FAR const struct fb_cmap_s *src)
{
	int n;

	for (n = src->first; n < src->len && n < STM32_LTDC_CLUT_ENTRIES; n++) {
		regvdbg("n = %d, red = %02x, green = %02x, blue = %02x\n", n, src->red[n], src->green[n], src->blue[n]);
		dest->red[n] = src->red[n];
		dest->green[n] = src->green[n];
		dest->blue[n] = src->blue[n];
	}

	dest->first = src->first;
	dest->len = src->len;
}
#endif

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: stm32_getvideoinfo
 *
 * Description:
 *   Get the videoinfo for the framebuffer
 *
 * Parameter:
 *   vtable - The framebuffer driver object
 *   vinfo  - the videoinfo object
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_getvideoinfo(struct fb_vtable_s *vtable, struct fb_videoinfo_s *vinfo)
{
	gvdbg("vtable=%p vinfo=%p\n", vtable, vinfo);
	if (vtable) {
		FAR struct ltdc_layer_s *ltdc;
#ifdef CONFIG_STM32L4_LTDC_L2
		ltdc = (FAR struct ltdc_layer_s *)&LAYER_L2;
#else
		ltdc = (FAR struct ltdc_layer_s *)&LAYER_L1;
#endif
		return stm32_lgetvideoinfo(ltdc, vinfo);
	}

	gdbg("ERROR: Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getplaneinfo
 *
 * Description:
 *   Get the planeinfo for the framebuffer
 *
 * Parameter:
 *   vtable - The framebuffer driver object
 *   pinfo  - the planeinfo object
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_getplaneinfo(struct fb_vtable_s *vtable, int planeno, struct fb_planeinfo_s *pinfo)
{
	gvdbg("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo);
	if (vtable) {
		FAR struct ltdc_layer_s *ltdc;
#ifdef CONFIG_STM32L4_LTDC_L2
		ltdc = (FAR struct ltdc_layer_s *)&LAYER_L2;
#else
		ltdc = (FAR struct ltdc_layer_s *)&LAYER_L1;
#endif
		return stm32_lgetplaneinfo(ltdc, planeno, pinfo);
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getcmap
 *
 * Description:
 *   Get a range of CLUT values for the LCD
 *
 * Parameter:
 *   vtable - The framebuffer driver object
 *   cmap   - the color table
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

#ifdef STM32_LAYER_CLUT_SIZE
static int stm32_getcmap(struct fb_vtable_s *vtable, struct fb_cmap_s *cmap)
{
#ifdef CONFIG_STM32L4_LTDC_L2
	return stm32_getclut((FAR struct ltdc_layer_s *)&LAYER_L2, cmap);
#else
	return stm32_getclut((FAR struct ltdc_layer_s *)&LAYER_L1, cmap);
#endif
}

/****************************************************************************
 * Name: stm32_putcmap
 *
 * Description:
 *   Set a range of the CLUT values for the LCD
 *
 * Parameter:
 *   vtable - The framebuffer driver object
 *   cmap   - the color table
 *
 * Return:
 * On success - OK
 * On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_putcmap(struct fb_vtable_s *vtable, const struct fb_cmap_s *cmap)
{
#ifdef CONFIG_STM32L4_LTDC_L2
	return stm32_setclut((FAR struct ltdc_layer_s *)&LAYER_L2, cmap);
#else
	return stm32_setclut((FAR struct ltdc_layer_s *)&LAYER_L1, cmap);
#endif
}
#endif							/* STM32_LAYER_CLUT_SIZE */

/****************************************************************************
 * Name: stm32_lgetvideoinfo
 *
 * Description:
 *   Get video information about the layer
 *
 * Parameter:
 *   layer  - Reference to the layer control structure
 *   vinfo  - Reference to the video info structure
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_lgetvideoinfo(struct ltdc_layer_s *layer, struct fb_videoinfo_s *vinfo)
{
	gvdbg("layer=%p vinfo=%p\n", layer, vinfo);
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;

	if (priv == &LAYER_L1 || priv == &LAYER_L2) {
		memcpy(vinfo, &priv->state.vinfo, sizeof(struct fb_videoinfo_s));

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_lgetplaneinfo
 *
 * Description:
 *   Get plane information about the layer
 *
 * Parameter:
 *   layer   - Reference to the layer control structure
 *   planeno - Number of the plane
 *   pinfo   - Reference to the plane info structure
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_lgetplaneinfo(struct ltdc_layer_s *layer, int planeno, struct fb_planeinfo_s *pinfo)
{
	gvdbg("layer=%p planeno=%d pinfo=%p\n", layer, planeno, pinfo);
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;

	if (stm32_ltdc_lvalidate(priv) && planeno == 0) {
		memcpy(pinfo, &priv->state.pinfo, sizeof(struct fb_planeinfo_s));
		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_setclut
 *
 * Description:
 *   Configure layer clut (color lookup table).
 *   Non clut is defined during initializing.
 *   Clut is active during next vertical blank period. Do not need an update.
 *
 * Parameter:
 *   layer  - Reference to the layer structure
 *   cmap   - color lookup table with up the 256 entries
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

#ifdef STM32_LAYER_CLUT_SIZE
static int stm32_setclut(struct ltdc_layer_s *layer, const struct fb_cmap_s *cmap)
{
	int ret;
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer=%p cmap=%p\n", layer, cmap);

	if (stm32_ltdc_lvalidate(priv) && cmap) {
		sem_wait(priv->state.lock);

		if (priv->state.vinfo.fmt != FB_FMT_RGB8) {
			gdbg("Error: CLUT is not supported for the pixel format: %d\n", priv->state.vinfo.fmt);
			ret = -EINVAL;
		} else if (cmap->first > STM32_LTDC_CLUT_ENTRIES) {
			gdbg("Error: CLUT offset is out of range: %d\n", cmap->first);
			ret = -EINVAL;
		} else {
			/* Copy to the layer cmap */

			stm32_ltdc_cmapcpy(priv->state.cmap, cmap);

			/* Update layer clut register */

			stm32_ltdc_lclut(priv);

			ret = OK;
		}

		sem_post(priv->state.lock);

		return ret;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getclut
 *
 * Description:
 *   Get configured layer clut (color lookup table).
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   cmap  - Reference to valid color lookup table accept up the 256 color
 *           entries
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_getclut(struct ltdc_layer_s *layer, struct fb_cmap_s *cmap)
{
	int ret;
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer=%p cmap=%p\n", layer, cmap);

	if (priv == &LAYER_L1 || priv == &LAYER_L2) {
		sem_wait(priv->state.lock);

		if (priv->state.vinfo.fmt != FB_FMT_RGB8) {
			gdbg("Error: CLUT is not supported for the pixel format: %d\n", priv->state.vinfo.fmt);
			ret = -EINVAL;
		} else if (cmap->len < priv->state.cmap->len) {
			gdbg("Error: cmap must accept %d color table entries: %d\n", priv->state.cmap->len);
			ret = -EINVAL;
		} else {
			/* Copy from the layer cmap */

			stm32_ltdc_cmapcpy(cmap, priv->state.cmap);

			ret = OK;
		}

		sem_post(priv->state.lock);

		return ret;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}
#endif							/* STM32_LAYER_CLUT_SIZE */

#ifdef CONFIG_STM32L4_LTDC_INTERFACE
/****************************************************************************
 * Name: getlid
 *
 * Description:
 *   Get a specific layer identifier.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   lid   - Reference to store the layer id
 *   flag  - Operation flag describe the layer identifier
 *           e.g. get the current active or inactive layer.
 *           See LTDC_LAYER_* for possible values
 *
 * Return:
 *   OK - On success
 *   Null if invalid flag
 *
 ****************************************************************************/

static int stm32_getlid(FAR struct ltdc_layer_s *layer, int *lid, uint32_t flag)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;

	gvdbg("flag = %08x\n", flag);

	if (stm32_ltdc_lvalidate(priv)) {
		int ret = OK;

		sem_wait(priv->state.lock);

		switch (flag) {
		case LTDC_LAYER_OWN:
			*lid = priv->state.lid;
			break;
#ifdef CONFIG_STM32L4_LTDC_L2
		case LTDC_LAYER_ACTIVE:
			*lid = g_lactive;
			break;
		case LTDC_LAYER_INACTIVE:
			*lid = !g_lactive;
			break;
		case LTDC_LAYER_TOP:
			*lid = LTDC_LAYER_L2;
			break;
		case LTDC_LAYER_BOTTOM:
			*lid = LTDC_LAYER_L1;
			break;
#else
		case LTDC_LAYER_ACTIVE:
		case LTDC_LAYER_INACTIVE:
		case LTDC_LAYER_TOP:
		case LTDC_LAYER_BOTTOM:
			*lid = LTDC_LAYER_L1;
			break;
#endif
		default:
			ret = EINVAL;
			gdbg("Returning EINVAL\n");
			break;
		}

		sem_post(priv->state.lock);

		return ret;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_setcolor
 *
 * Description:
 *    Configure layer default color value for the non active layer area.
 *    Default value during initializing: 0x00000000
 *    Color is active after next update.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   argb  - ARGB8888 color value
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 *
 ****************************************************************************/

static int stm32_setcolor(FAR struct ltdc_layer_s *layer, uint32_t argb)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, argb = %08x\n", layer, argb);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		priv->state.color = argb;
		priv->operation |= LTDC_LAYER_SETCOLOR;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getcolor
 *
 * Description:
 *   Get configured layer color for the non active layer area.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   argb  - Reference to store the ARGB8888 color value
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
*******************************************************************************/

static int stm32_getcolor(FAR struct ltdc_layer_s *layer, uint32_t *argb)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, argb = %p\n", layer, argb);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		*argb = priv->state.color;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_setcolorkey
 *
 * Description:
 *    Configure layer default color key (chromakey) value for transparency.
 *    Layer default value during initializing: 0x00000000
 *    Colorkey is active after next update.
 *
 * Parameter:
 *   layer  - Reference to the layer structure
 *   rgb   - RGB888 color value
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_setcolorkey(FAR struct ltdc_layer_s *layer, uint32_t rgb)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, argb = %08x\n", layer, rgb);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		priv->state.colorkey = rgb;
		priv->operation |= LTDC_LAYER_SETCOLORKEY;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getcolorkey
 *
 * Description:
 *   Get the configured layer color key (chromakey) for transparency.
 *
 * Parameter:
 *   layer  - Reference to the layer structure
 *   rgb    - Reference to store the RGB888 color key
 *
 * Return:
 *   On success - OK
 *   On error   - -EINVAL
 *
 ****************************************************************************/

static int stm32_getcolorkey(FAR struct ltdc_layer_s *layer, uint32_t *rgb)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, argb = %p\n", layer, rgb);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		*rgb = priv->state.colorkey;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: setalpha
 *
 * Description:
 *   Configure layer alpha value factor into blend operation.
 *   During the layer blend operation the source alpha value is multiplied
 *   with this alpha value. If the source color format doesn't support alpha
 *   channel (e.g. non ARGB8888) this alpha value will be used as constant
 *   alpha value for blend operation.
 *   Default alpha value during initializing: 0xff
 *   Alpha is active after next update.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   alpha - Alpha value
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 *
 ****************************************************************************/

static int stm32_setalpha(FAR struct ltdc_layer_s *layer, uint8_t alpha)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, alpha = %02x\n", layer, alpha);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		priv->state.alpha = alpha;
		priv->operation |= LTDC_LAYER_SETALPHAVALUE;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getalpha
 *
 * Description:
 *   Get configured layer alpha value factor for blend operation.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   alpha - Reference to store the alpha value
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 *
 ****************************************************************************/

static int stm32_getalpha(FAR struct ltdc_layer_s *layer, uint8_t *alpha)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, alpha = %p\n", layer, alpha);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		*alpha = priv->state.alpha;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: setblendmode
 *
 * Description:
 *   Configure blend mode of the layer.
 *   Default mode during initialzing: LTDC_BLEND_NONE
 *   Blendmode is active after next update.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   mode  - Blend mode (see LTDC_BLEND_*)
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 *
 * Procedure information:
 *   LTDC_BLEND_NONE:
 *     Informs the driver to disable all blend operation for the given layer.
 *     That means the layer is opaque. Note this has no effect on the
 *     colorkey settings.
 *
 *   LTDC_BLEND_ALPHA:
 *     Informs the driver to enable alpha blending for the given layer.
 *
 *   LTDC_BLEND_COLORKEY:
 *     Informs the driver to enable colorkeying for the given layer.
 *
 *   LTDC_BLEND_SRCPIXELALPHA:
 *     Informs the driver to use the pixel alpha value of the layer instead
 *     the constant alpha value. This is only useful for ARGB8888
 *     color format.
 *
 *   LTDC_BLEND_DESTPIXELALPHA:
 *     Informs the driver to use the pixel alpha value of the subjacent layer
 *     instead the constant alpha value. This is only useful for ARGB8888
 *     color format.
 *
 ****************************************************************************/

static int stm32_setblendmode(FAR struct ltdc_layer_s *layer, uint32_t mode)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, mode = %08x\n", layer, mode);

	if (stm32_ltdc_lvalidate(priv)) {
		int ret = OK;

		sem_wait(priv->state.lock);

		/* Disable alpha blending by default */

		priv->bf1 = LTDC_BF1_CONST_ALPHA;
		priv->bf2 = LTDC_BF2_CONST_ALPHA;
		stm32_ltdc_lsetopac(priv);

		/* Disable colorkeying by default */

		priv->operation &= ~LTDC_LAYER_ENABLECOLORKEY;

		if (mode & LTDC_BLEND_ALPHA) {
			/* Initialize blend factor */

			if (mode & LTDC_BLEND_SRCPIXELALPHA) {
				priv->bf1 = LTDC_BF1_PIXEL_ALPHA;
			} else if (mode & LTDC_BLEND_DESTPIXELALPHA) {
				priv->bf2 = LTDC_BF2_PIXEL_ALPHA;
			}

			/* Enable blending, restore the alpha value */

			stm32_ltdc_lunsetopac(priv);
			mode &= ~LTDC_BLEND_ALPHA;
		}

		if (mode & LTDC_BLEND_COLORKEY) {
			/* Enable colorkeying */

			priv->operation |= LTDC_LAYER_ENABLECOLORKEY;
			mode &= ~LTDC_BLEND_COLORKEY;
		}

		if (mode & ~(LTDC_BLEND_SRCPIXELALPHA | LTDC_BLEND_DESTPIXELALPHA)) {
			gdbg("Unknown blendmode %02x\n", mode);
			ret = -EINVAL;
		}

		if (ret == OK) {
			priv->state.blendmode = mode;
			priv->operation |= (LTDC_LAYER_SETBLENDMODE | LTDC_LAYER_SETALPHAVALUE | LTDC_LAYER_SETCOLORKEY);
		}

		sem_post(priv->state.lock);

		return ret;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getblendmode
 *
 * Description:
 *   Get configured blend mode of the layer.
 *
 * Parameter:
 *   layer - Reference to the layer structure
 *   mode  - Reference to store the blend mode
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 ****************************************************************************/

static int stm32_getblendmode(FAR struct ltdc_layer_s *layer, uint32_t *mode)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, mode = %p\n", layer, mode);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		*mode = priv->state.blendmode;
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_setarea
 *
 * Description:
 *   Configure visible layer area and the reference position of the first
 *   pixel of the whole layer which is the first visible top left pixel in
 *   the active area.
 *   Area is active after next update.
 *
 * Parameter:
 *   layer   - Reference to the layer control structure
 *   area    - Reference to the valid area structure for the new active area
 *   srcxpos - x position of the visible pixel of the whole layer
 *   srcypos - y position of the visible pixel of the whole layer
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 *
 * Procedure Information:
 *   If the srcxpos and srcypos unequal the xpos and ypos of the area
 *   structure this acts like moving the visible area to another position on
 *   the screen during the next update operation.
 *
 ****************************************************************************/

static int stm32_setarea(FAR struct ltdc_layer_s *layer, FAR const struct ltdc_area_s *area, fb_coord_t srcxpos, fb_coord_t srcypos)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, area = %p, srcxpos = %d, srcypos = %d\n", layer, area, srcxpos, srcypos);

	if (stm32_ltdc_lvalidate(priv)) {
		int ret;

		sem_wait(priv->state.lock);

		ret = stm32_ltdc_lvalidatearea(priv, area->xpos, area->ypos, area->xres, area->yres, srcxpos, srcypos);

		if (ret == OK) {
			priv->state.xpos = srcxpos;
			priv->state.ypos = srcypos;
			priv->state.area.xpos = area->xpos;
			priv->state.area.ypos = area->ypos;
			priv->state.area.xres = area->xres;
			priv->state.area.yres = area->yres;
			priv->operation |= LTDC_LAYER_SETAREA;
		}

		sem_post(priv->state.lock);

		return ret;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_getarea
 *
 * Description:
 *    Get configured visible layer area.
 *
 * Parameter:
 *   layer   - Reference to the layer control structure
 *   area    - Reference to the area structure to store the active area
 *   srcxpos - Reference to store the referenced x position of the whole layer
 *   srcypos - Reference to store the reterenced y position of the whole layer
 *
 * Return:
 *   On success - OK
 *   On error - -EINVAL
 *
 ****************************************************************************/

static int stm32_getarea(FAR struct ltdc_layer_s *layer, FAR struct ltdc_area_s *area, fb_coord_t *srcxpos, fb_coord_t *srcypos)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
	gvdbg("layer = %p, area = %p, srcxpos = %p, srcypos = %p\n", layer, area, srcxpos, srcypos);

	if (stm32_ltdc_lvalidate(priv)) {
		sem_wait(priv->state.lock);
		*srcxpos = priv->state.xpos;
		*srcypos = priv->state.ypos;
		memcpy(area, &priv->state.area, sizeof(struct ltdc_area_s));
		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 * Name: stm32_update
 *
 * Description:
 *   Update current layer settings and make changes visible.
 *
 * Parameter:
 *   layer   - Reference to the layer structure
 *   mode    - operation mode
 *
 * Return:
 *    OK     - On success
 *   -EINVAL - If one of the parameter invalid
 *
 * Procedure information:
 *   LTDC_UPDATE_SIM:
 *     Informs the driver to update both layers simultaneously. Otherwise update
 *     the given layer only.
 *
 *   LTDC_UPDATE_FLIP:
 *     Informs the driver to perform a flip operation.
 *     This only effects the ltdc layer 1 and 2 and can be useful for double
 *     buffering. Each flip operation changed the active layer to the inactive
 *     and vice versa. In the context of the ltdc that means, the inactive layer
 *     is complete disabled. So the subjacent layer is the background layer
 *     (background color). To reactivate both layer and their current settings
 *     perform an update without LTDC_UPDATE_FLIP flag.
 *
 *   LTDC_UPDATE_ACTIVATE:
 *     Informs the driver that the given layer should be the active layer when
 *     the operation is complete.
 *
 *   LTDC_SYNC_VBLANK:
 *     Informs the driver to update the layer upon vertical blank. Otherwise
 *     immediately.
 *
 ****************************************************************************/

static int stm32_update(FAR struct ltdc_layer_s *layer, uint32_t mode)
{
	FAR struct stm32_layer_s *priv = (FAR struct stm32_layer_s *)layer;
#ifdef CONFIG_STM32L4_LTDC_L2
	FAR struct stm32_layer_s *active = &LAYER(g_lactive);
	FAR struct stm32_layer_s *inactive = &LAYER(!g_lactive);
#endif

	gvdbg("layer = %p, mode = %08x\n", layer, mode);

	if (stm32_ltdc_lvalidate(priv)) {
		/* Reload immediately by default */

		uint8_t reload = LTDC_SRCR_IMR;

		sem_wait(priv->state.lock);

		if (mode & LTDC_SYNC_VBLANK) {
			reload = LTDC_SRCR_VBR;
		}

		if (mode & LTDC_SYNC_WAIT) {
			reload |= LTDC_SRCR_WAIT;
		}

		/* Update the given layer */

		stm32_ltdc_lupdate(priv);

#ifdef CONFIG_STM32L4_LTDC_L2
		/* The following operation only useful if layer 2 is supported.
		 * Otherwise ignore it.
		 */

		if (mode & LTDC_UPDATE_SIM) {
			/* Also update the flip layer */

			stm32_ltdc_lupdate(&LAYER(!priv->state.lid));
		}

		if (mode & LTDC_UPDATE_ACTIVATE) {
			/* Set the given layer to the next active layer */

			g_lactive = priv->state.lid;

			/* Also change the current active layer for flip operation */

			active = &LAYER(!g_lactive);
		}

		if (mode & LTDC_UPDATE_FLIP) {
			/* Reset if manipulated by ACTIVATE flag */

			inactive = &LAYER(!active->state.lid);

			/* Set blendfactor for current active layer to there reset value */

			stm32_ltdc_lblendmode(active, STM32_LTDC_BF1_RESET, STM32_LTDC_BF2_RESET);

			/* Set blendfactor for current inactive layer */

			stm32_ltdc_lblendmode(inactive, inactive->bf1, inactive->bf2);

			/* Disable the active layer */

			active->operation &= ~LTDC_LAYER_ENABLE;

			stm32_ltdc_lenable(active);

			/* Enable the inactive layer */

			inactive->operation |= LTDC_LAYER_ENABLE;

			stm32_ltdc_lenable(inactive);

			/* Ensure that both layer active and the manipulated layer
			 * settings restored during the next update (non flip) operation
			 */

			active->operation |= (LTDC_LAYER_SETBLENDMODE | LTDC_LAYER_ENABLE | LTDC_LAYER_SETCOLOR | LTDC_LAYER_SETENABLE);

			/* Change layer activity */

			g_lactive = inactive->state.lid;
		}
#endif

		/* Make the changes visible */

		stm32_ltdc_reload(reload);

		sem_post(priv->state.lock);

		return OK;
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

#ifdef CONFIG_STM32L4_DMA2D
/****************************************************************************
 * Name: blit
 *
 * Description:
 *   Copy selected area from a background layer to selected position of the
 *   foreground layer. Copies the result to the destination layer.
 *
 * Parameter:
 *   dest     - Reference to the destination layer
 *   fore     - Reference to the foreground layer
 *   forexpos - Selected x target position of the destination layer
 *   foreypos - Selected y target position of the destination layer
 *   back     - Reference to the background layer
 *   backarea - Reference to the selected area of the background layer
 *
 * Return:
 *    OK     - On success
 *   -EINVAL - If one of the parameter invalid or if the size of the selected
 *             source area outside the visible area of the destination layer.
 *             (The visible area usually represents the display size)
 *
 ****************************************************************************/

static int stm32_blit(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea)
{
	FAR struct stm32_layer_s *destlayer = (FAR struct stm32_layer_s *)dest;
	FAR struct stm32_layer_s *forelayer = (FAR struct stm32_layer_s *)fore;
	FAR struct stm32_layer_s *backlayer = (FAR struct stm32_layer_s *)back;

	gvdbg("dest = %p, fore = %p, forexpos = %d, foreypos = %d, back = %p, backarea = %p\n", dest, fore, forexpos, foreypos, back, backarea);

	if (stm32_ltdc_lvalidate(destlayer) && stm32_ltdc_lvalidate(forelayer) && stm32_ltdc_lvalidate(backlayer)) {
		return stm32_dma2dblit(&destlayer->state, &forelayer->state, forexpos, foreypos, &backlayer->state, backarea);
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}

/****************************************************************************
 *
 * Name: blend
 *
 * Description:
 *   Blends the selected area from a background layer with selected position of
 *   the foreground layer. Blends the result with the destination layer.
 *   Note! This is the same as the blit operation but with blending depending on
 *   the blendmode settings of the layer.
 *
 * Parameter:
 *   dest     - Reference to the destination layer
 *   fore     - Reference to the foreground layer
 *   forexpos - Selected x target position of the destination layer
 *   foreypos - Selected y target position of the destination layer
 *   back     - Reference to the background layer
 *   backarea - Reference to the selected area of the background layer
 *
 * Return:
 *    OK     - On success
 *   -EINVAL - If one of the parameter invalid or if the size of the selected
 *             source area outside the visible area of the destination layer.
 *             (The visible area usually represents the display size)
 *
 ****************************************************************************/

static int stm32_blend(FAR struct ltdc_layer_s *dest, FAR struct ltdc_layer_s *fore, fb_coord_t forexpos, fb_coord_t foreypos, FAR struct ltdc_layer_s *back, FAR const struct ltdc_area_s *backarea)
{
	FAR struct stm32_layer_s *destlayer = (FAR struct stm32_layer_s *)dest;
	FAR struct stm32_layer_s *forelayer = (FAR struct stm32_layer_s *)fore;
	FAR struct stm32_layer_s *backlayer = (FAR struct stm32_layer_s *)back;

	gvdbg("dest = %p, fore = %p, forexpos = %d, foreypos = %d, back = %p, backarea = %p\n", dest, fore, forexpos, foreypos, back, backarea);

	if (stm32_ltdc_lvalidate(destlayer) && stm32_ltdc_lvalidate(forelayer) && stm32_ltdc_lvalidate(backlayer)) {
		return stm32_dma2dblend(&destlayer->state, &forelayer->state, forexpos, foreypos, &backlayer->state, backarea);
	}

	gdbg("Returning EINVAL\n");
	return -EINVAL;
}
#endif

/****************************************************************************
 * Name: stm32_ltdcgetlayer
 *
 * Description:
 *   This is a non-standard framebuffer interface.
 *   Get the specific layer object by the layer id to enable layer hardware
 *   support.
 *
 * Parameter:
 *   lid - Layer identifier
 *
 * Return:
 *   Reference to the layer control structure on success or Null if lid
 *   is invalid.
 *
 ****************************************************************************/

FAR struct ltdc_layer_s *stm32_ltdcgetlayer(int lid)
{
	gvdbg("lid: %d\n", lid);
	if (lid == LTDC_LAYER_L1 || lid == LTDC_LAYER_L2) {
		return (FAR struct ltdc_layer_s *)&LAYER(lid);
	}

	gdbg("EINVAL\n");
	errno = EINVAL;
	return NULL;
}
#endif							/* CONFIG_STM32L4_LTDC_INTERFACE */

/****************************************************************************
 * Name: stm32_ltdcinitialize
 *
 * Description:
 *   Initialize the ltdc controller
 *
 * Return:
 *   OK
 *
 ****************************************************************************/
#ifdef USE_HAL_DRIVER
/* Physical frame buffer for background and foreground layers */
/* 390*390 pixels with 32bpp - 20% */
#if defined ( __ICCARM__ )  /* IAR Compiler */
#pragma data_alignment = 16
uint32_t              PhysFrameBuffer[121680];
#elif defined (__GNUC__)    /* GNU Compiler */
uint32_t              PhysFrameBuffer[121680] __attribute__ ((aligned (16)));
#else                       /* ARM Compiler */
__align(16) uint32_t  PhysFrameBuffer[121680];
#endif
#define BUFFER_ADD (0x60000000UL)
uint8_t stm32l4_ltdc_initialize(void)
{
    (void)irq_attach(STM32L4_IRQ_LCD_TFT, (xcpt_t)up_ltdc_isr, NULL);
    (void)irq_attach(STM32L4_IRQ_LCD_TFT_ER, (xcpt_t)up_ltdc_er_isr, NULL);

    __HAL_LTDC_RESET_HANDLE_STATE(&hltdc);
    hltdc.Instance = LTDC;
    hltdc.Init.HSPolarity         = LTDC_HSPOLARITY_AL;
    hltdc.Init.VSPolarity         = LTDC_VSPOLARITY_AL;
    hltdc.Init.DEPolarity         = LTDC_DEPOLARITY_AL;
    hltdc.Init.PCPolarity         = LTDC_PCPOLARITY_IPC;
    hltdc.Init.HorizontalSync     = 0;   /* HSYNC width - 1 */
    hltdc.Init.VerticalSync       = 0;   /* VSYNC width - 1 */
    hltdc.Init.AccumulatedHBP     = 1;   /* HSYNC width + HBP - 1 */
    hltdc.Init.AccumulatedVBP     = 1;   /* VSYNC width + VBP - 1 */
    hltdc.Init.AccumulatedActiveW = 391; /* HSYNC width + HBP + Active width - 1 */
    hltdc.Init.AccumulatedActiveH = 391; /* VSYNC width + VBP + Active height - 1 */
    hltdc.Init.TotalWidth         = 392; /* HSYNC width + HBP + Active width + HFP - 1 */
    hltdc.Init.TotalHeigh         = 392; /* VSYNC width + VBP + Active height + VFP - 1 */
    hltdc.Init.Backcolor.Red      = 0;   /* Not used default value */
    hltdc.Init.Backcolor.Green    = 0;   /* Not used default value */
    hltdc.Init.Backcolor.Blue     = 0;   /* Not used default value */
    hltdc.Init.Backcolor.Reserved = 0xFF;
    if(HAL_LTDC_Init(&hltdc) != HAL_OK) {
        return(LCD_ERROR);
    }

    /* LTDC layer 1 configuration */
    hlayer.WindowX0        = 0;
    hlayer.WindowX1        = 390;
    hlayer.WindowY0        = 0;
    hlayer.WindowY1        = 390;
    hlayer.PixelFormat     = LTDC_PIXEL_FORMAT_ARGB8888;
    hlayer.Alpha           = 0xFF; /* NU default value */
    hlayer.Alpha0          = 0; /* NU default value */
    hlayer.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA; /* Not Used: default value */
    hlayer.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA; /* Not Used: default value */
    hlayer.FBStartAdress   = (uint32_t)(0x60000000);//PhysFrameBuffer;
    hlayer.ImageWidth      = 390; /* virtual frame buffer contains 768 pixels per line for 24bpp */
    /* (192 blocs * 16) / (24bpp/3) = 1024 pixels per ligne        */
    hlayer.ImageHeight     = 390;
    hlayer.Backcolor.Red   = 0; /* Not Used: default value */
    hlayer.Backcolor.Green = 0; /* Not Used: default value */
    hlayer.Backcolor.Blue  = 0; /* Not Used: default value */
    hlayer.Backcolor.Reserved = 0xFF;
    if(HAL_LTDC_ConfigLayer(&hltdc, &hlayer, LTDC_LAYER_1) != HAL_OK) {
        return(LCD_ERROR);
    }
    return LCD_OK;
}
#else
int stm32l4_ltdc_initialize(void)
{
	gvdbg("Initialize LTDC driver\n");

	/* Disable the LCD */
	stm32_lcd_enable(false);

	gvdbg("Configuring the LCD controller\n");

	/* Configure LCD periphery */
	gvdbg("Configure lcd periphery\n");
	stm32_ltdc_periphconfig();

	/* Configure global ltdc register */
	gvdbg("Configure global register\n");
	stm32_global_configure();

	/* Initialize ltdc layer */

	gvdbg("Initialize ltdc layer\n");
	stm32_ltdc_linit(LTDC_LAYER_L1);
#ifdef CONFIG_STM32L4_LTDC_L2
	stm32_ltdc_linit(LTDC_LAYER_L2);
#endif

	/* Display layer 1 and 2 */

	stm32_ltdc_lenable(&LAYER_L1);
#ifdef CONFIG_STM32L4_LTDC_L2
	stm32_ltdc_lenable(&LAYER_L2);
#endif
	/* Enable the backlight */
#ifdef CONFIG_STM32L4_LCD_BACKLIGHT
	stm32l4_backlight(true);
#endif
	/* Reload shadow register */
	gvdbg("Reload shadow register\n");
	stm32_ltdc_reload(LTDC_SRCR_IMR);

	/* Turn the LCD on */
	gvdbg("Enabling the display\n");
	stm32_lcd_enable(true);
	return OK;
}
#endif
/****************************************************************************
 * Name: stm32_ltdcgetvplane
 *
 * Description:
 *   Return a a reference to the framebuffer object for the specified video
 *   plane.
 *
 * Input parameters:
 *   None
 *
 * Returned value:
 *   Reference to the framebuffer object (NULL on failure)
 *
 ****************************************************************************/

struct fb_vtable_s *stm32l4_ltdcgetvplane(int vplane)
{
	gvdbg("vplane: %d\n", vplane);
	if (vplane == 0) {
		return (struct fb_vtable_s *)&g_vtable;
	}

	return NULL;
}

/****************************************************************************
 * Name: fb_uninitialize
 *
 * Description:
 *   Uninitialize the framebuffer driver.  Bad things will happen if you
 *   call this without first calling fb_initialize()!
 *
 ****************************************************************************/

void stm32l4_ltdc_uninitialize(void)
{
	/* Disable the LCD controller */

	stm32_lcd_enable(false);
}

/****************************************************************************
 * Name:  stm32_lcdclear
 *
 * Description:
 *   This is a non-standard LCD interface just for the STM32 LTDC. Clearing the
 *   display in the normal way by writing a sequences of runs that covers the
 *   entire display can be slow.  Here the display is cleared by simply setting
 *   all video memory to the specified color.
 *
 * Parameter:
 *   color - The color the clear the whole framebuffer
 *
 ****************************************************************************/

void stm32l4_lcdclear(uint16_t color)
{
#ifdef CONFIG_STM32L4_LTDC_L2
	stm32_ltdc_lclear(&LAYER(LTDC_LAYER_L2), color);
#endif
	stm32_ltdc_lclear(&LAYER(LTDC_LAYER_L1), color);
}

/****************************************************************************
 * Name: stm32_lcd_backlight
 *
 * Description:
 *   Provide this interface to turn the backlight on and off.
 *
 * Parameter:
 *   blon - Enable or disable the lcd backlight
 *
 ****************************************************************************/

#ifdef CONFIG_STM32L4_LCD_BACKLIGHT
void stm32l4_backlight(bool blon)
{
	/* Set default backlight level CONFIG_STM32L4_LTDC_DEFBACKLIGHT */

	gdbg("Not supported\n");
}
#endif

int up_ltdc_isr(int irq, uint32_t *regs)
{
    HAL_LTDC_IRQHandler(&hltdc);
    return 0;
}

int up_ltdc_er_isr(int irq, uint32_t *regs)
{
    return 0;
}

